iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 12
10
Modern Web

平時沒注意的 JavaScript - JS 生態系及週邊工具整理系列 第 13

NPM Install 到底做了些什麼?node_modules 檔案結構 + 特性入門

  • 分享至 

  • xImage
  •  

Hi 大家好,我們昨天介紹了在你的專案中使用 NPM 幾個最基本的步驟

npm initnpm install

我們可以注意到,npm init 執行的動作很簡單

那就只是幫我們創建 package.json 的 boilerplate

而像是有廣大生態系的 npm,其實 package.json 裡面的內容也是包山包海的

最後的進度是到,我們發現了 npm install 後有三樣東西改變了

  1. package.json 記錄了你剛剛裝的套件
{
  "name": "my-project",
  "version": "1.0.0",
  "description": "this is my project",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "alxtz",
  "license": "ISC",
  "dependencies": {
    "selenium-webdriver": "^3.6.0"
  }
}
  1. node_modules 裡面除了剛剛安裝的 selenium-webdriver,還安裝了許多其他的套件

  2. 以及莫名其妙出現的 package-lock.json

(備註: 我這裡使用的是 npm 第 5.6.0 版,如果你發現 npm install 的結果不同,建議至少升級至 npm 5.0.0 以上)

為什麼 npm 要紀錄我們裝過哪些套件?

記得我最早在寫 Python 時

許多小的 Project,會在 README 裡面寫

這個 Project 需要使用哪個 Dependency

有寫還好,我頂多使用 pip 一個一個安裝

但是遇到什麼資訊都沒有的 Project,你就只得等執行後

看執行錯誤,把相對應的套件裝起來

這邊 npm 為了方便

他會在你每次 npm install 時

都將有安裝的套件寫入 package.json

這樣以後別人要使用你的 code

會可以很輕易的知道該使用哪些 dependency

而且還可以確定使用的版本

(Python 後來有使用了 venv 這樣的 Dependency 管理機制,後面會提到)

為什麼會有 node_modules 這個資料夾

這邊有個有意思的地方

那就是,跟 python 使用 pip 不太一樣

在 python 裡,使用 pip 安裝過 selenium 的話

你會發現,你之後再同台電腦上

不管寫什麼程式,都可以直接 import selenium

這個原因是,pip 預設會將這個函式庫裝在類似 usr/local/lib 這樣的地方

讓你所有寫的 Python 檔,預設都去一個地方讀這些函式庫

裝在 usr/local/lib 哪裡不好?

這樣最大的問題是

當你 Project 有一個以上時

有時候不同的需求

可能會使用到同個函式庫的第 2 版 或 第 3 版

這時候,因為你的 usr/local/lib 只能存在一個版本的 selenium

你每次都可能得把它覆蓋掉

並且

當你想把你的 code 放到 server 端去跑時

你很難一眼看出這一堆 usr/local/lib 的函式庫

有哪些是被你專案所需要的,其他是其他用途的

npm 的做法

這邊 npm 解決這個問題的做法

是每個專案要使用的函式庫

都直接安裝在他的資料夾底下

保持每個環境(資料夾),永遠都不會有相衝突的機會

而這個資料夾就是 node_modules

(你可能會覺得這樣有重複的函式庫,豈不是會浪費很多空間。
沒錯! npm 這樣本地儲存的方式的確比較耗費空間
但是他同時也避免了上述的全部問題
所以後來像是 Python 社群,也自己開發了一套工具 venv
他可以把 pip 預設的安裝路徑變成這個專案資料夾底下
並且創建一份 requirements.txt 來記錄所有依賴
而在 Ruby 裡,比較接近的東西會是 bundle)

如何解決函式庫龐大的問題

這邊其實就非常簡單了

有了 package.json 紀錄我們所有的函式庫

我們之後想把 Project 放到另一個環境時

我們只需要複製我們的原始碼,和 package.json

之後執行 npm install,如果後面不加套件名稱的話

npm 會當作是你想把 package.json 上現在缺的 Dependency 都裝回來

這邊是一個範例

首先我先使用了 tree node_modules -L 1 這個指令

來展示 node_modules 下面有的所有 dependency

接著,我使用 rm -rf node_modules 來把 node_modules 整個砍掉

但是我們會發現在再執行 npm install 之後

我們之前所需要的函式庫都回來了

這可以幫助 npm 跟 git 相容

因為你不需要把整個龐大的 node_modules 都丟進版本控制裡

只要把 package.json 儲存起來就好了!

npm 怎麼會安裝一大堆其他的 dependency ?

如果你有注意到的話,package.json 雖然只有紀錄 selenium-webdriver 一個套件

可是我們卻安裝了 30 個函式庫

node_modules
├── balanced-match/
├── brace-expansion/
├── concat-map/
├── core-js/
├── core-util-is/
├── es6-promise/
├── fs.realpath/
├── glob/
├── immediate/
├── inflight/
├── inherits/
├── isarray/
├── jszip/
├── lie/
├── minimatch/
├── once/
├── os-tmpdir/
├── pako/
├── path-is-absolute/
├── process-nextick-args/
├── readable-stream/
├── rimraf/
├── sax/
├── selenium-webdriver/
├── string_decoder/
├── tmp/
├── util-deprecate/
├── wrappy/
├── xml2js/
└── xmlbuilder/

(還只有一個是 selenium)

原因是,(這裡的)selenium-webdriver 本身也是使用 node.js 開發的專案

他本身八九不離十也是使用 package.json 來管理他的依賴

我們這邊可以實際進入 node_modules/selenium-webdriver 來看看

可以發現,selenium-webdriver 因為也使用 npm 當作 package manager

他的檔案結構跟我們的專案很像

而他的 package.json 也同樣地寫著 selenium-webdriver 所有會用到的 dependency

{
...
  "dependencies": {
    "jszip": "^3.1.3",
    "rimraf": "^2.5.4",
    "tmp": "0.0.30",
    "xml2js": "^0.4.17"
  },
  "devDependencies": {
    "express": "^4.14.0",
    "mocha": "^3.1.2",
    "multer": "^1.2.0",
    "promises-aplus-tests": "^2.1.2",
    "serve-index": "^1.8.0",
    "sinon": "^1.17.6"
  },
...
  "name": "selenium-webdriver",
...
}

(這邊要注意的欄位有 devdependencies 和 dependencies,詳細差別後續篇章會介紹)

selenium 這個專案也有用到像是 jszip, express 這樣的套件

可是在他的資料夾底下,卻沒有 node_modules 來給 selenium 使用

原因

原因是我們是把 selenium 當作依賴加入我們的資料夾

npm 聰明的地方是,他在安裝 selenium 時

會順便把 package.json 讀過一遍

如果看到有其他 dependency,就會在遞迴(重複)性的去安裝這些 dependency

像是假設 jszip 又需要其他 dependency,將會一直安裝直到全部都有為止

這也是 npm install 為什麼會花比較久時間的原因

明天

希望今天的介紹可以讓你對 npm 有一個基本,但是扎實的理解

明天我們會介紹 package-lock.json 這個檔案,與 npm 的競爭對手,yarn

有任何問題都歡迎提問


上一篇
史上最強套件管理 - NPM , npm init 與 npm install (Day11)
下一篇
package-lock.json 有什麼用,淺談 Yarn, NPM5 與 npm shrinkwrap
系列文
平時沒注意的 JavaScript - JS 生態系及週邊工具整理33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

1
Will Wang
iT邦新手 5 級 ‧ 2019-02-12 16:15:57

Alex, 你這篇真的寫的很棒,我喜歡這裡介紹的層次。

0
Ho.Chun
iT邦新手 5 級 ‧ 2019-05-05 23:52:28

因果關係 與 優缺比較 都有
/images/emoticon/emoticon41.gif

我要留言

立即登入留言